/** @noSelfInFile **/
import {
    IModelAdapter,
    modelAdapter,
    modelCenter,
    ModelCenter,
    ModelType
} from "../../adapter/adapterInterface/model5/IModelFactory";
import {effectAdapter} from "../../adapter/adapterInterface/model2/IEffectAdapter";
import {HandleModel} from "../model1/HandleModel";
import {EffectModel} from "../model2/EffectModel";
import {TexttagModel} from "../model2/TexttagModel";
import {TimerModel} from "../model2/TimerModel";
import {RectModel} from "../model2/RectModel";
import {UnitModel} from "../model3/UnitModel";
import {ItemModel} from "../model3/ItemModel";
import {Logger} from "../../config/Logger";
import {LocModel} from "../../base/LocModel";
import Configer from "../../config/Configer";
import {ItemJson, UnitJson} from "../../model/StoreJson";
import {ItemDataKey} from "../../../dataSource/DataSource";
import {ConfigKey} from "../../../../constant/ConfigConstant";
import {DataRouter} from "../../../dataSource/DataRouter";
import {PlayerModel} from "../model4/PlayerModel";
import {FinalKey} from "../../../../constant/FinalConstant";
import {CodeUtil} from "../../../codeAdapter/CodeUtil";
import {unitAdapter} from "../../adapter/adapterInterface/model3/IUnitAdapter";
import {itemAdapter} from "../../adapter/adapterInterface/model3/IItemAdapter";
import {PlayerFactory} from "../../../PlayerFactory";
import MathUtil from "../../../codeAdapter/MathUtil";
import {DataRouterUtil} from "../../../../utils/router/DataRouterUtil";


export class ModelFactory {

    static modelFactory: ModelFactory = new ModelFactory();
    modelCenter: ModelCenter = modelCenter;
    modelAdapter: IModelAdapter;

    static getInstance() {
        return ModelFactory.modelFactory;
    }

    constructor() {
    }

    init() {
        this.modelAdapter = modelAdapter.adapter;
    }

    addListen = (id: string, handleModel: HandleModel, type: ModelType) => {
       return this.modelCenter.addListen(id, handleModel, type);
    }
    removeListen = (handleModel: HandleModel, type: ModelType) => {
        this.modelCenter.removeListen(handleModel, type);
    }
    getModel = (id: string, type: ModelType) => {
        return this.modelCenter.getModel(id, type);
    }

    getRectModel = (id: string): RectModel => {
        let rectModel: RectModel = this.getModel(id, ModelType.rect) as RectModel;
        if (rectModel) {
            return rectModel;
        }
        const newRectModel = this.createRectModel(id);
        return newRectModel;
    }

    /**
     * 创建模型
     */
    createUnit = (playerId: number, unitJson: UnitJson, loc: LocModel, face: number): UnitModel => {
        const handleModel = new UnitModel(unitJson.id);
        const unitData = DataRouter.getUnitDataByUnitId(unitJson.id)
        handleModel.createHandle(playerId, unitJson, unitData, loc, face);
        const ret = this.addListen(handleModel.getHandleId(), handleModel, ModelType.unit);
        if (ret) {
            return handleModel;
        }
        Logger.toastError(`单位${unitJson.id}: 创建失败，可能是一个无效的单位或id重复`)
    };

    registUnit (this: void, handle: any) {
        const name = unitAdapter.adapter.getName(handle);
        const handleModel = new UnitModel(name);
        const strId = unitAdapter.adapter.getStrId(handle);
        const unitData = DataRouter.getUnitDataByUnitId(strId);
        handleModel.bindHandle(handle, unitData);
        const ret = ModelFactory.getInstance().addListen(handleModel.getHandleId(), handleModel, ModelType.unit);
        if (ret) {
            return handleModel;
        }
    }

    createEffectInWidget = (modelName: string, unitModel: UnitModel, point: string): EffectModel => {
        const adapter = effectAdapter.adapter;
        const effect = adapter.createHandleOnUnit(modelName, unitModel, point);
        const handleModel = new EffectModel(effect, modelName);
        const ret = this.addListen(handleModel.getHandleId(), handleModel, ModelType.effect);
        if (ret) {
            return handleModel;
        }
        Logger.toastError(`特效${modelName}: 创建失败，可能是一个无效的单位或id重复`)
    }
    createEffectInPoint = (modelName: string, loc: LocModel): EffectModel => {
        const adapter = effectAdapter.adapter;
        const effect = adapter.createHandleOnLoc(modelName, loc);
        const handleModel = new EffectModel(effect, modelName);
        this.addListen(handleModel.getHandleId(), handleModel, ModelType.effect);
        return handleModel;
    }
    createItemInUnit = (itemJson: ItemJson, unitModel: UnitModel, seller?: UnitModel, addAffix?: boolean): ItemModel => {
        const handleModel = new ItemModel(itemJson.id);
        const itemData = DataRouter.getItemDataByItemId(itemJson.id)
        handleModel.createHandle(itemJson, itemData, unitModel.loc);
        if (addAffix && CodeUtil.isNullOrZero(itemJson.af)) {
            DataRouterUtil.addNewAffixToItem(handleModel);
        }
        if (seller) {
            handleModel.data[ItemDataKey.seller] = seller;
        }
        const ret = this.addListen(handleModel.getHandleId(), handleModel, ModelType.item);
        if (ret) {
            this.unitAddItem(unitModel, handleModel);
            return handleModel;
        }
        Logger.toastError(`物品${itemJson.id}: 创建失败，可能是一个无效的单位或id重复`)
    }
    createItemInPoint = (itemJson: ItemJson, loc: LocModel, addAffix?: boolean): ItemModel => {
        const handleModel = new ItemModel(itemJson.id);
        const itemData = DataRouter.getItemDataByItemId(itemJson.id)
        handleModel.createHandle(itemJson, itemData, loc);
        if (addAffix && CodeUtil.isNullOrZero(itemJson.af)) {
            DataRouterUtil.addNewAffixToItem(handleModel);
        }
        const ret = this.addListen(handleModel.getHandleId(), handleModel, ModelType.item);
        if (ret) {
            return handleModel;
        }
        Logger.toastError(`物品${itemJson.id}: 创建失败，可能是一个无效的单位或id重复`)
    }
    // todo 【需要被回退】
    registItem = (handle: any) => {
        const name = itemAdapter.adapter.getName(handle);

        const strId = itemAdapter.adapter.getStrId(handle);
        const itemData = DataRouter.getItemDataByItemId(strId);
        const handleModel = new ItemModel(name);
        handleModel.bindHandle(handle, itemData);
        const ret = this.addListen(handleModel.getHandleId(), handleModel, ModelType.item);
        if (ret) {
            return handleModel;
        }
    }
    createRectModel = (id: string): RectModel => {
        const handleModel = new RectModel(id);

        this.addListen(handleModel.getHandleId(), handleModel, ModelType.rect);

        return handleModel;
    }
    // createSound = (fileName: string, looping: boolean, is3D: boolean, stopWhenOutOfRange: boolean, fadeInRate: number, fadeOutRate: number, eaxSetting: string) => {
    //     const handleModel = new SoundModel(fileName, looping, is3D, stopWhenOutOfRange, fadeInRate, fadeOutRate, eaxSetting);
    //     this.addListen(handleModel.getHandleId(), handleModel, ModelType.sound);
    //     return handleModel;
    // }
    createTexttag = (text: string, size: number, loc: LocModel) => {
        const handleModel = new TexttagModel(text, size, loc);
        this.addListen(handleModel.getHandleId(), handleModel, ModelType.text);
        return handleModel;
    }
    createTimer = () => {
        const handleModel = new TimerModel('');
        this.addListen(handleModel.getHandleId(), handleModel, ModelType.other);
        return handleModel;
    }
    /**
     * 工具
     */
    consumItemOnUnit = (targetUnit: UnitModel, itemId: string, useFamily: boolean, num: number) => {
        let tempNum = num;
        let success = false;
        const delItems: ItemModel[] = [];


        const callBack: (item: ItemModel, index: number) => boolean = function (itemModel: ItemModel, index: number) {
            if (itemModel?.isValid()) {
            } else {
                return false;
            }
            let itemStrId = itemModel.strId;

            if (!CodeUtil.isNullOrZero(useFamily)) {
                const itemData = DataRouter.getItemDataByItemId(itemStrId);
                if (itemData) {
                    itemStrId = itemData.family_tpye;
                }
            }

            if (itemStrId === itemId) {
            } else {
                return false;
            }
            if (!CodeUtil.isNullOrZero(tempNum)) {
                let currentNum = itemModel.charges;
                // 当前物品的剩余数量（数量为0时默认为有一个）
                if (currentNum === 0) {
                    currentNum = 1;
                }
                if (currentNum >= tempNum) {
                    if (currentNum > tempNum) {
                        itemModel.charges = currentNum - tempNum;
                        tempNum = 0;
                    } else {
                        delItems.push(itemModel);
                    }
                    success = true;
                } else {
                    tempNum = tempNum - currentNum;
                    delItems.push(itemModel);
                }
            }
            if (success) {
                return true
            }
        }

        const player = PlayerFactory.getInstance().getUnitOwner(targetUnit);
        if (player?.isNotEndPlaying()) {
            player.mapBags(callBack)
        }
        targetUnit.mapItems(callBack)

        if (success) {
            for (let i = 0;i < delItems.length; i++) {
                const item = delItems[i];
                if (item?.isValid()) {
                    ModelFactory.getInstance().unitRemoveItem(targetUnit, item);
                }
            }
        }
        return success;
    }
    initRectUnit = () => {
        const allMap = Configer.getFinal(FinalKey.allMapRect);
        const rect = ModelFactory.getInstance().getRectModel(allMap);
        const units = this.modelAdapter.initRectUnit(rect, this.registUnit);
        return units
    }
    mapUnitInRect = (name: string, fun: (unit: UnitModel) => void) => {
        const rect = ModelFactory.getInstance().getRectModel(name);
        this.modelAdapter.mapUnitInRect(rect, fun)
    }
    mapUnitInRange(loc: LocModel, radius: number, fun: (unit: UnitModel) => void) {
        this.modelAdapter.mapUnitInRange(loc, radius, fun)
    }
    mapItemsInRange(loc: LocModel, radius: number, callBack: (item: ItemModel) => void) {
        this.modelAdapter.mapItemsInRange(loc, radius, callBack)
    }
    mapItemsInRect(rect: RectModel, callBack: (item: ItemModel) => void) {
        this.modelAdapter.mapItemsInRect(rect, callBack)
    }

    getSelectedPlayer(player: PlayerModel) {
        const unitArr: UnitModel[] = [];
        if (player) {
            this.modelAdapter.mapPlayerSelected(player, (unit) => {
                if (unit) {
                    unitArr.push(unit);
                }
            })
        }
        return unitArr;
    }

    mapAllItemModelClear () {
        const itemMap = this.modelCenter.modelMap[ModelType.item];
        const keys = Object.keys(itemMap);
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            const model = this.getModel(key, ModelType.item) as ItemModel
            if (model) {
                if (model.isAlive()) {
                } else {
                    this.destroy(model, ModelType.item)
                }
            }
        }
    }

    getUnitFirstItem(unit: UnitModel): ItemModel {
        return unit.getFirstItem();
        // return this.modelAdapter.getUnitFirstItem(unit);
    }

    rectContainsUnit(rect: RectModel, unit: UnitModel) {
        return this.modelAdapter.rectContainsUnit(rect, unit);
    }

    unitAddItem(unit: UnitModel, item: ItemModel, index?: number) {
        if (item?.isValid()) {
            let ret = false;
            const items = this.getItemModelsByUnit(unit);
            const maxSlot = Configer.getConfig(ConfigKey.maxItemSlot);
            for (let i = 0; i < maxSlot; i++) {
                const targetItem = items[i];
                const itemData = item?.template;
                if (!CodeUtil.isNullOrZero(itemData.stackable)) {
                    const itemStrId = item?.strId;
                    const tempStrId = targetItem?.strId;
                    if (itemStrId === tempStrId) {
                        targetItem.charges = targetItem.charges + item.charges;
                        this.unitRemoveItem(unit, item)
                        ret = true;
                        break;
                    }
                }
            }
            if (ret) {
            } else {
               ret = unit.giveItem(item, index);
            }
        }
    }

    unitRemoveItem(unit: UnitModel, item: ItemModel) {
        unit?.clearItem(item);
        this.destroy(item, ModelType.item);
    }

    sellItem(unit: UnitModel, item: ItemModel) {
        const gold = item.template.goldcost;
        const lumber = item.template.lumbercost;
        const player = PlayerFactory.getInstance().getUnitOwner(unit);
        if (!CodeUtil.isNullOrZero(gold)) {
            player.addGold(MathUtil.toNumber(gold / 2))
        }
        if (!CodeUtil.isNullOrZero(lumber)) {
            player.addLumber(MathUtil.toNumber(lumber / 2))
        }
        ModelFactory.getInstance().unitRemoveItem(unit, item);
    }
    // todo 【需要被回退】 需要使用UnitModel中的mapItem
    getItemModelsByUnit(unit: UnitModel): ItemModel[] {
        if (unit?.isValid()) {
        } else {
            return [];
        }
        const goods: ItemModel[] = [];
        const maxSlot = Configer.getConfig(ConfigKey.maxItemSlot);
        for (let i = 0; i < maxSlot; i++) {
            const item = UnitItemInSlotBJ(unit.handle, i + 1);
            const itemId = `${GetHandleId(item)}`;
            const itemModel = this.getModel(itemId, ModelType.item) as ItemModel;
            if (itemModel?.isValid()) {
                goods.push(itemModel);
            }
        }
        return goods;
    }

    /**
     * 销毁
     * 销毁物品优先使用 this.unitRemoveItem 方法
     */
    destroy = (handleModel: HandleModel, type: ModelType) => {
        this.removeListen(handleModel, type);
        if (handleModel) {
            handleModel.destroy();
        }
    }

}